package mytask.controller;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;
import javax.validation.Valid;

import mytask.dao.ProjectDAO;
import mytask.dao.ProjectMemberDAO;
import mytask.dao.TicketDAO;
import mytask.dao.UserDAO;
import mytask.domain.AuthManager;
import mytask.domain.TranslationManager;
import mytask.model.Page;
import mytask.model.Project;
import mytask.model.ProjectMember;
import mytask.model.ProjectStatus;
import mytask.model.Ticket;
import mytask.model.User;
import mytask.model.UserRole;
import mytask.view.Screen;
import mytask.view.ScreenManager;

import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class ProjectController {

	private static final int PROJECTS_PER_PAGE = 20;

	private static final int TICKETS_PER_PAGE = 20;

	@Resource
	private ProjectMemberDAO memberDAO;

	@Resource
	private ProjectDAO projectDAO;

	@Resource
	private TicketDAO ticketDAO;

	@Resource
	private UserDAO userDAO;

	public void setProjectMemberDAO(ProjectMemberDAO memberDAO) {
		this.memberDAO = memberDAO;
	}

	public void setProjectDAO(ProjectDAO projectDAO) {
		this.projectDAO = projectDAO;
	}

	public void setTicketDAO(TicketDAO ticketDAO) {
		this.ticketDAO = ticketDAO;
	}

	public void setUserDAO(UserDAO userDAO) {
		this.userDAO = userDAO;
	}

	@RequestMapping(value = "/projects", method = RequestMethod.GET)
	public ModelAndView showProjectMembership() {
		if (!AuthManager.isUserLoggedIn()) {
			return new ModelAndView(ScreenManager.REDIRECT_HOME);
		}

		final ModelAndView screen = ScreenManager.get(Screen.PROJECTS_HOME);
		screen.addObject("additionalStyles", new String[] { "Project.css" });

		final User user = AuthManager.getPageUser();

		final List<Project> myProjects = projectDAO.listAcceptedProjects(user, PROJECTS_PER_PAGE);
		screen.addObject("projects", myProjects);

		return screen;
	}

	@RequestMapping(value = "/projects/view", method = RequestMethod.GET)
	public ModelAndView showProject(@RequestParam(value = "id") Integer id) {
		if (!AuthManager.isUserLoggedIn()) {
			return new ModelAndView(ScreenManager.REDIRECT_HOME);
		}

		final Project project = projectDAO.getProject(id);
		if (project != null) {
			final Map<String, Object> params = new HashMap<String, Object>();
			params.put("project-id", project.getId());
			params.put("project-title", project.getTitle());

			final ModelAndView screen = ScreenManager.get(Screen.PROJECT_VIEW, params);
			screen.addObject("additionalStyles", new String[] { "Project.css", "Ticket.css" });

			final String title = "#" + project.getId() + ": " + project.getTitle();
			screen.addObject(ScreenManager.SCREEN_TITLE, title);
			screen.addObject("project", project);
			screen.addObject("projectStatus", project.getStatus().toString());

			final List<Ticket> tickets = ticketDAO.listTickets(project, 0, TICKETS_PER_PAGE);
			screen.addObject("tickets", tickets);

			screen.addObject("paging", createPaging(project, 1));

			return screen;
		}

		return ScreenManager.getErrorMessage(TranslationManager.get("message.project-not-found"));
	}

	@RequestMapping(value = "/projects/ticket-list", method = RequestMethod.GET)
	public ModelAndView getTickets(@RequestParam(value = "project-id") Integer id,
			@RequestParam(value = "page-nr") Integer pageNr) {
		final Project project = projectDAO.getProject(id);
		if (project != null) {
			final ModelAndView screen = ScreenManager.get(Screen.TICKET_LIST);
			screen.addObject("project", project);

			final int offset = (pageNr - 1) * TICKETS_PER_PAGE;
			final List<Ticket> tickets = ticketDAO.listTickets(project, offset, TICKETS_PER_PAGE);
			screen.addObject("tickets", tickets);

			screen.addObject("paging", createPaging(project, pageNr));

			return screen;
		}

		return ScreenManager.getErrorMessage(TranslationManager.get("message.project-not-found"));
	}

	@RequestMapping(value = "/projects/edit", method = RequestMethod.GET)
	public ModelAndView editProject(@RequestParam(value = "id") Integer id) {
		if (!AuthManager.isUserLoggedIn()) {
			return new ModelAndView(ScreenManager.REDIRECT_HOME);
		}

		final Project project = projectDAO.getProject(id);
		if (project != null) {
			final Map<String, Object> params = new HashMap<String, Object>();
			params.put("project-id", project.getId());
			params.put("project-title", project.getTitle());

			final ModelAndView screen = ScreenManager.get(Screen.PROJECT_EDIT, params);
			screen.addObject("additionalStyles", new String[] { "Project.css" });

			final String title = "#" + project.getId() + ": " + project.getTitle();
			screen.addObject(ScreenManager.SCREEN_TITLE, title);

			final List<ProjectMember> members = memberDAO.listProjectMembers(project);
			screen.addObject("userList", members);
			screen.addObject("statusList", ProjectStatus.values());
			screen.addObject("project", project);

			return screen;
		}

		return ScreenManager.getErrorMessage(TranslationManager.get("message.project-not-found"));
	}

	@RequestMapping(value = "/projects/edit", method = RequestMethod.POST)
	public ModelAndView saveProject(@Valid @ModelAttribute("project") Project project, BindingResult results) {
		if (!AuthManager.isUserLoggedIn()) {
			return new ModelAndView(ScreenManager.REDIRECT_HOME);
		}

		if (results.hasErrors()) {
			if (project.getId() == null)
				return addProject();
			else
				return editProject(project.getId());
		}

		project.setLastModified(new Date());
		projectDAO.saveProject(project);

		if (memberDAO.getProjectMember(AuthManager.getPageUser(), project) == null) {
			final ProjectMember owner = new ProjectMember();
			owner.setRole(UserRole.OWNER);
			owner.setUser(AuthManager.getPageUser());
			owner.setProject(project);
			memberDAO.saveProjectMember(owner);
		}

		final List<ProjectMember> membersList = new ArrayList<ProjectMember>();
		User user;
		ProjectMember added;
		String[] emails = project.getRawMembers().split(";");
		for (int i = 0; i < emails.length; i++) {
			emails[i] = emails[i].trim().toLowerCase();
			if (emails[i].isEmpty())
				continue;
			user = userDAO.getUserByEmail(emails[i]);
			if (user != null) {
				added = new ProjectMember();
				added.setProject(project);
				added.setUser(user);
				added.setRole(UserRole.WAITING);
				membersList.add(added);
			} else {
				results.rejectValue("rawMembers", "members_err", TranslationManager.get("message.invalid-members")
						+ emails[i]);
				return editProject(project.getId());
			}
		}

		for (ProjectMember member : membersList) {
			if (memberDAO.getProjectMember(member.getUser(), project) == null)
				memberDAO.saveProjectMember(member);
		}

		return showProject(project.getId());

	}

	@RequestMapping(value = "/projects/accept", method = RequestMethod.GET)
	public ModelAndView acceptProject(@RequestParam(value = "id") long id) {
		if (!AuthManager.isUserLoggedIn()) {
			return new ModelAndView(ScreenManager.REDIRECT_HOME);
		}

		final Project project = projectDAO.getProject((int) id);
		final ProjectMember member = memberDAO.getProjectMember(AuthManager.getPageUser(), project);
		member.setRole(UserRole.MEMBER);
		memberDAO.saveProjectMember(member);
		return (new ModelAndView(ScreenManager.REDIRECT_HOME));
	}

	@RequestMapping(value = "/projects/reject", method = RequestMethod.GET)
	public ModelAndView rejectProject(@RequestParam(value = "id") long id) {
		if (!AuthManager.isUserLoggedIn()) {
			return new ModelAndView(ScreenManager.REDIRECT_HOME);
		}

		final Project project = projectDAO.getProject((int) id);
		final ProjectMember member = memberDAO.getProjectMember(AuthManager.getPageUser(), project);
		member.setRole(UserRole.REJECTED);
		memberDAO.saveProjectMember(member);
		return (new ModelAndView(ScreenManager.REDIRECT_HOME));
	}

	@RequestMapping(value = "/projects/add", method = RequestMethod.GET)
	public ModelAndView addProject() {
		if (!AuthManager.isUserLoggedIn()) {
			return new ModelAndView(ScreenManager.REDIRECT_HOME);
		}

		final ModelAndView screen = ScreenManager.get(Screen.PROJECT_ADD);
		screen.addObject("additionalStyles", new String[] { "Project.css" });

		screen.addObject(ScreenManager.PAGE_TITLE, TranslationManager.get("title.project-add"));
		screen.addObject(ScreenManager.SCREEN_TITLE, TranslationManager.get("title.project-add"));

		final Project newProject = new Project();
		screen.addObject("project", newProject);

		return screen;
	}

	private List<Page> createPaging(final Project project, final int pageNr) {
		int numTickets = ticketDAO.numTickets(project);
		int page = 1;
		final List<Page> paging = new ArrayList<Page>();
		while (numTickets > 0) {
			paging.add(new Page(page, page == pageNr));
			page++;
			numTickets -= TICKETS_PER_PAGE;
		}

		return paging;
	}

}
